package com.fulizhe.catlocal.integration.springboot.metric;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.dianping.cat.CatConstants;
import com.dianping.cat.local.CatLocalMessageTreeDealer;
import com.dianping.cat.local.persistence.CatLogServiceFactory;
import com.dianping.cat.message.Event;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.message.spi.MessageTree;
import com.fulizhe.catlocal.integration.springboot.entity.CatLogInfoEntity;
import com.fulizhe.catlocal.integration.springboot.mapper.CatLogInfoMapper;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EscapeUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 使用CAT官方的日志存储规则进行日志收集, 进而提供日志的对外查询功能
 * 
 * @author LQ
 *
 */
public class DefaultCatLocalMessageTreeDealer3 implements CatLocalMessageTreeDealer {

	@Override
	public void deal(MessageTree tree) {
		saveLogToDb(tree);
		// 似乎有問題, 目前還是不進行双写了....
		//CatLogServiceFactory.getSingleton().saveLog(tree);
	}

	private void saveLogToDb(MessageTree tree) {
		if (!(tree.getMessage() instanceof Transaction)) {
			return;
		}

		// 以下大部分判断应该在更早期就抛弃掉
		Transaction transaction = (Transaction) tree.getMessage();
		if (!transaction.getType().equals("URL")) {
			return;
		}
		final String url = Convert.toStr(transaction.getName(), "");
		if (url.endsWith(".html") || url.endsWith(".js") || url.endsWith(".css") || url.startsWith("/h2/")) {
			return;
		}

		InnerCatSpringUtil.getBean(CatLogInfoMapper.class).insert(transfer(tree));
	}

	CatLogInfoEntity transfer(MessageTree tree) {
		Transaction transaction = (Transaction) tree.getMessage();

		final HashMap<String, String> map = new HashMap<String, String>();
		loopMessage(transaction, map);

		final CatLogInfoEntity entity = new CatLogInfoEntity();
		entity.setTraceid(tree.getMessageId());
		entity.setElapse((int) transaction.getDurationInMillis());
		entity.setDatee(DateUtil.date(transaction.getTimestamp()).toJdkDate());
		entity.setLoginfo(tree.toString());
		entity.setUrl((String) transaction.getName());
		entity.setTypee(Convert.toBool(map.get("isFail"), false) ? "Exception" : "Normal");
		entity.setParam(StrUtil.subWithLength(map.getOrDefault("param", ""), 0, 500));
		entity.setResult(StrUtil.subWithLength(map.getOrDefault("result", ""), 0, 500));
		return entity;
	}

	protected void loopMessage(Message message, Map<String, String> sr) {
		if (message instanceof Transaction) {
			Transaction transaction = (Transaction) message;
			List<Message> children = transaction.getChildren();

			if (!children.isEmpty()) {
				int len = children.size();
				for (int i = 0; i < len; i++) {
					Message child = children.get(i);
					if (child != null) {
						loopMessage(child, sr);
					}
				}
			}
		} else if (message instanceof Event) {
			final Event event = (Event) message;

			if (isParamEvent(event)) {
				sr.put("param", Convert.toStr(message.getData()));
			} else if (isResultEvent(event)) {
				String result = Convert.toStr(message.getData());
				if (StrUtil.isNotEmpty(result) && result.contains("<")) {
					result = EscapeUtil.escape(result);
				}
				sr.put("result", result);
			} else if (hasException(event)) {
				sr.put("isFail", "true");
			}

		}
	}

	private static final String[] EXCEPTION_TYPES = new String[] { "Error", "RuntimeException", "Exception" };

	private boolean hasException(Event event) {
		String eventType = event.getType();
		return ArrayUtil.contains(EXCEPTION_TYPES, eventType);
	}

	private boolean isParamEvent(Event event) {
		final String type = event.getType();
		final String name = event.getName();

		return (type.equals(CatConstants.TYPE_URL) || type.equals(CatConstants.TYPE_URL_FORWARD))
				&& (name.equals(CatConstants.TYPE_URL + ".param")
						|| name.equals(CatConstants.TYPE_URL_FORWARD + ".param"));
	}

	private boolean isResultEvent(Event event) {
		final String type = event.getType();
		final String name = event.getName();

		return (type.equals(CatConstants.TYPE_URL) || type.equals(CatConstants.TYPE_URL_FORWARD))
				&& (name.equals(CatConstants.TYPE_URL + ".result")
						|| name.equals(CatConstants.TYPE_URL_FORWARD + ".result"));
	}

}